package mx.itesm.twritter.server.parser;
 
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
 
public class StringMatchingNonExact {
 
  private List<String> calles = new ArrayList<String>();
 
  private Map<String, Double> matchingScores = new HashMap<String, Double>();
 
  private Scanner scanner;
 
  private static double[][] mismatchScoreTable;
 
  private String in;
 
  private int inLength;
  
  private String street;
 
  public StringMatchingNonExact(String fileName, String street) {
 
    try {
      scanner = new Scanner(new FileReader(fileName));
      while (scanner.hasNext()) {
        this.calles.add(scanner.nextLine());
      }
    } catch (FileNotFoundException e) {
      e.printStackTrace();
      System.exit(1);
    }
 
    if (mismatchScoreTable == null) initMismatchScoreTable();
    matchStreet(street);
    System.out.println(this.street);
  }
 
  public List<String> getcalles() {
    return this.calles;
  }
 
  private static void initMismatchScoreTable() {
 
    mismatchScoreTable = new double[256][256];
 
    // 1 by default.
    for (int i = 0; i < 256; i++)
      for (int j = 0; j < 256; j++)
        mismatchScoreTable[i][j] = 1.0d;
 
    // input charater and reference character are the same.
    for (int i = 0; i < 256; i++)
      mismatchScoreTable[i][i] = 0.0d;
    
    // ignore cases.
    for (int i = 65; i < 91; i++) {
    	mismatchScoreTable[i][i+32] = 0.0d;
        mismatchScoreTable[i+32][i] = 0.0d;
    }
 
    // accents
    mismatchScoreTable['a']['á'] = 0.0d;
    mismatchScoreTable['á']['a'] = 0.0d;
    
    mismatchScoreTable['e']['é'] = 0.0d;
    mismatchScoreTable['é']['e'] = 0.0d;
    
    mismatchScoreTable['i']['í'] = 0.0d;
    mismatchScoreTable['í']['i'] = 0.0d;
    
    mismatchScoreTable['o']['ó'] = 0.0d;
    mismatchScoreTable['ó']['o'] = 0.0d;
    
    mismatchScoreTable['u']['ú'] = 0.0d;
    mismatchScoreTable['ú']['u'] = 0.0d;
    
    // common typpos
    mismatchScoreTable['e']['a'] = 0.1d;
    mismatchScoreTable['a']['e'] = 0.1d;
 
    mismatchScoreTable['o']['u'] = 0.1d;
    mismatchScoreTable['u']['o'] = 0.1d;
    
    mismatchScoreTable['v']['b'] = 0.15d;
    mismatchScoreTable['b']['v'] = 0.15d;
 
    mismatchScoreTable['n']['m'] = 0.11d;
    mismatchScoreTable['m']['n'] = 0.11d;
 
    mismatchScoreTable['t']['r'] = 0.15d;
    mismatchScoreTable['r']['t'] = 0.15d;
 
    mismatchScoreTable['g']['h'] = 0.15d;
    mismatchScoreTable['h']['g'] = 0.15d;
 
    mismatchScoreTable['y']['u'] = 0.15d;
    mismatchScoreTable['u']['y'] = 0.15d;
 
  }
 
  public Map<String, Double> getScores(String in) {
    this.in = in;
    this.inLength = in.length();
 
    for (String calle : this.calles) {
 
      int refLength = calle.length();
 
      double[][] errScore = new double[inLength + 1][refLength + 1];
      errScore[0][0] = 0.0d;
 
      for (int inCharAt = 1; inCharAt <= this.inLength; inCharAt++)
        errScore[inCharAt][0] = inCharAt;
 
      for (int refCharAt = 1; refCharAt <= refLength; refCharAt++)
        errScore[0][refCharAt] = refCharAt;
 
      for (int inCharAt = 1; inCharAt <= inLength; inCharAt++)
        for (int refCharAt = 1; refCharAt <= refLength; refCharAt++) {
          double charAbsence = errScore[inCharAt - 1][refCharAt] + 1;
 
          double charRedundance = errScore[inCharAt][refCharAt - 1] + 1;
 
          double mismatch = errScore[inCharAt - 1][refCharAt - 1]
                  + mismatchScoreTable[this.in.charAt(inCharAt - 1)][calle
                          .charAt(refCharAt - 1)];
 
          double charPositionSwap = 999999d;
 
          if (inCharAt > 1
                  && refCharAt > 1
                  && this.in.charAt(inCharAt - 1) == calle
                          .charAt(refCharAt - 2)
                  && this.in.charAt(inCharAt - 2) == calle
                          .charAt(refCharAt - 1)) {
 
            if (this.in.charAt(inCharAt - 2) == 'e'
                    && this.in.charAt(inCharAt - 1) == 'i') {
              charPositionSwap = errScore[inCharAt - 2][refCharAt - 2] + 0.25;
            }
 
 
            else charPositionSwap = errScore[inCharAt - 2][refCharAt - 2] + 0.5;
          }
 
 
          double minScore = mismatch;
 
          if (charAbsence < minScore) {
            minScore = charAbsence;
          }
 
          if (charRedundance < minScore) {
            minScore = charRedundance;
          }
 
          if (charPositionSwap < minScore) {
            minScore = charPositionSwap;
          }
 
          errScore[inCharAt][refCharAt] = minScore;
        }
      this.matchingScores.put(calle, errScore[this.inLength][refLength]);
    }
    return this.matchingScores;
  }
 
  
  public void matchStreet(String street) {
	  Map scoreMap = this.getScores(street);
	  double minScore = (Double) Collections.min(scoreMap.values());
	  if (minScore == 0.0d) {
	        this.street = street;
	      } else {
	        List<String> corrections = new ArrayList<String>();
	        for (String calle : this.getcalles()) {
	          if (scoreMap.get(calle).equals(minScore)) {
	        	  this.street = calle;
	          }
	        }
	      }
	  
  }
  
  public String getStreet() {
	  return street;
	  
  }
}